package com.avcompris.util;

import static com.avcompris.util.ExceptionUtils.nonNullArgument;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * utility class to build listener composites.
 * 
 * @author David Andriana Copyright Avantage Compris SARL 2009 ©
 */
public abstract class ListenerUtils extends AbstractUtils {

	/**
	 * get a listener composite from a list of listeners.
	 * 
	 * @param clazz the listener's class.
	 * @param listeners the listeners to add.
	 */
	public static <T> T createListenerComposite(
			final Class<T> clazz,
			final T... listeners) {

		nonNullArgument(clazz, "class");
		nonNullArgument(listeners, "listeners");

		final Object proxy = Proxy.newProxyInstance(Thread.currentThread()
				.getContextClassLoader(), new Class<?>[]{
			clazz
		}, new InvocationHandler() {

			@Override
			public Object invoke(
					final Object proxy,
					final Method method,
					final Object[] args) throws NoSuchMethodException {

				final String methodName = method.getName();
				final Class<?> returnType = method.getReturnType();

				final boolean hasNoArg = args == null || args.length == 0;

				final String completeMethodName = methodName + "("
						+ (hasNoArg ? "" : "...") + ")";

				if ("toString".equals(methodName) && hasNoArg) {

					return Arrays.toString(listeners);

				} else if (returnType != null && !Void.class.equals(returnType)
						&& !void.class.equals(returnType)) {

					throw new NoSuchMethodException(completeMethodName);

				} else {

					for (final T listener : listeners) {

						try {

							method.invoke(listener, args);

						} catch (final InvocationTargetException e) {
							throw new RuntimeException("Cannot invoke method: "
									+ completeMethodName + " on listener: "
									+ listener + " ("
									+ listener.getClass().getName() + ")");
						} catch (final IllegalAccessException e) {
							throw new RuntimeException("Cannot invoke method: "
									+ completeMethodName + " on listener: "
									+ listener + " ("
									+ listener.getClass().getName() + ")");
						}
					}
				}
				return null;
			}
		});

		return clazz.cast(proxy);
	}
}
